/* Canvas.cpp
Michael Zahniser, 06/10/09

Function definitions for the Canvas class.
*/

#include "Canvas.h"

#include <algorithm>

using std::min;
using std::max;
using std::swap;



Canvas::Canvas(Surface &surface, bool invertY)
	: pixels(surface)
{
	if(invertY)
	{
		begin = pixels.Begin(surface.Height() - 1);
		pitch = -pixels.Pitch();
	}
	else
	{
		begin = pixels.Begin(0);
		pitch = pixels.Pitch();
	}
}



void Canvas::SetPenColor(Uint32 color)
{
	penColor = color;
}



void Canvas::MoveTo(int x, int y)
{
	// Crop the line to the canvas surface.
	// For now, I'll just limit the coordinates to the canvas area.
	// TODO: proper cropping.
	x = max(0, min(pixels.Width() - 1, x));
	y = max(0, min(pixels.Height() - 1, y));

	penX = x;
	penY = y;
}



void Canvas::LineTo(int x, int y)
{
	// Crop the line to the canvas surface.
	// For now, I'll just limit the coordinates to the canvas area.
	// TODO: proper cropping.
	x = max(0, min(pixels.Width() - 1, x));
	y = max(0, min(pixels.Height() - 1, y));

	// Special cases: vertical and horizontal lines.
	if(y == penY)
	{
		// Horizontal line (or single point).
		int startX = max(0, min(x, penX));
		int steps = min(pixels.Width(), max(x, penX) + 1) - startX;
		
		Uint32 *it = begin + startX + y * pitch;
		Uint32 *end = it + steps;
		for( ; it != end; ++it)
			*it = penColor;
	}
	else if(x == penX)
	{
		// Vertical line.
		int startY = max(0, min(y, penY));
		int steps = min(pixels.Height(), max(y, penY) + 1) - startY;
		
		Uint32 *it = begin + x + startY * pitch;
		Uint32 *end = it + steps * pitch;
		for( ; it != end; it += pitch)
			*it = penColor;
	}
	else
	{
		int dx = x - penX;
		int dy = y - penY;
		
		int wholePitch = 1;
		int fractionPitch = pitch;
		
		if(dx < 0)
		{
			wholePitch = -wholePitch;
			dx = -dx;
		}
		if(dy < 0)
		{
			fractionPitch = -pitch;
			dy = -dy;
		}
		if(dx < dy)
		{
			swap(wholePitch, fractionPitch);
			swap(dx, dy);
		}
		
		int step = dy * 2;
		int fraction = dx;
		int whole = dx * 2;
		
		Uint32 *it = begin + penX + penY * pitch;
		Uint32 *end = begin + x + y * pitch;
		
		for( ; it != end; it += wholePitch)
		{
			*it = penColor;
			fraction += step;
			if(fraction >= whole)
			{
				fraction -= whole;
				it += fractionPitch;
			}
		}
		*it = penColor;
	}
	
	penX = x;
	penY = y;
}

